﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.ServiceProcess;
using System.Text;
using System.Xml;
using System.Management;

namespace Serviio.API
{
    public class ServiioConnection
    {
        private string _versionCache = null;

        public string Host { get; private set; }

        private const string ServiioServiceName = "Serviio";

        public ServiioConnection(string server = "127.0.0.1", string port = "23423")
        {
            Host = "http://" + server + ":" + port;
        }

        public string Version
        {
            get
            {
                if (_versionCache == null)
                {
                    try
                    {
                        var node = RESTHelper.Get(Host + "/rest/application").SelectSingleNode("/application/version");
                        _versionCache = node.InnerText;
                    }
                    catch { }
                }

                return _versionCache;
            }
        }

        public bool CheckMinVersion(string minVersion)
        {
            try
            {
                string[] minVersionA = minVersion.Split('.');
                int[] minVersionB = new int[minVersionA.Length];
                for (int i = 0; i < minVersionA.Length; i++)
                {
                    minVersionB[i] = int.Parse(minVersionA[i]);
                }

                string[] versionA = Version.Split('.');
                int[] versionB = new int[versionA.Length];
                for (int i = 0; i < versionA.Length; i++)
                {
                    versionB[i] = int.Parse(versionA[i]);
                }

                for (int i = 0; i < minVersionB.Length; i++)
                {
                    if (versionB.Length <= i || versionB[i] < minVersionB[i])
                        return false;
                    if (versionB[i] > minVersionB[i])
                        return true;
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool IsAvailable
        {
            get
            {
                try
                {
                    //return RESTHelper.Get(Host + "/rest/ping").SelectSingleNode("/result") != null;
                    return RESTHelper.Get(Host + "/rest/console-settings").SelectSingleNode("/consoleSettings") != null;
                }
                catch
                {
                    return false;
                }
            }
        }

        public ServiioServiceStatus ServiceStatus
        {
            get
            {
                try
                {
                    ServiceController sc = new ServiceController(ServiioServiceName);
                    if (sc.Status == ServiceControllerStatus.Running)
                    {
                        return ServiioServiceStatus.Started;
                    }
                    else
                    {
                        return ServiioServiceStatus.Stopped;
                    }
                }
                catch
                {
                    return ServiioServiceStatus.Unavailable;
                }
            }
        }

        public string ServiceFilePath
        {
            get
            {
                try
                {
                    RegistryKey pathKey = Registry.LocalMachine.OpenSubKey("System\\CurrentControlSet\\Services\\" + ServiioServiceName);
                    string exePath = pathKey.GetValue("ImagePath").ToString();
                    string path = exePath.Substring(0, exePath.LastIndexOf("\\"));
                    return path;
                }
                catch
                {
                    return "";
                }
            }
        }

        private ServiioStatus status = null;
        public ServiioStatus Status
        {
            get
            {
                if (status == null)
                    status = new ServiioStatus(this);
                return status;
            }
        }

        private ServiioRepository repository = null;
        public ServiioRepository Repository
        {
            get
            {
                if (repository == null)
                    repository = new ServiioRepository(this);
                return repository;
            }
        }

        private ServiioMetadata metadata = null;
        public ServiioMetadata Metadata
        {
            get
            {
                if (metadata == null)
                    metadata = new ServiioMetadata(this);
                return metadata;
            }
        }

        private ServiioPresentation presentation = null;
        public ServiioPresentation Presentation
        {
            get
            {
                if (presentation == null)
                    presentation = new ServiioPresentation(this);
                return presentation;
            }
        }

        private ServiioTranscoding transcoding = null;
        public ServiioTranscoding Transcoding
        {
            get
            {
                if (transcoding == null)
                    transcoding = new ServiioTranscoding(this);
                return transcoding;
            }
        }

        private ServiioReferenceData reference = null;
        public ServiioReferenceData Reference
        {
            get
            {
                if (reference == null)
                    reference = new ServiioReferenceData(this);
                return reference;
            }
        }

        public ServiioLibraryStatus GetLibraryStatus()
        {
            ServiioLibraryStatus status = new ServiioLibraryStatus();
            try
            {
                var xDoc = RESTHelper.Get(Host + "/rest/library-status");
                status.IsUpdatesCheckerRunning = bool.Parse(xDoc.SelectSingleNode("/libraryStatus/libraryUpdatesCheckerRunning").InnerText);
                status.IsAdditionsCheckerRunning = bool.Parse(xDoc.SelectSingleNode("/libraryStatus/libraryAdditionsCheckerRunning").InnerText);
                var lastAddedFileNameNode = xDoc.SelectSingleNode("/libraryStatus/lastAddedFileName");
                if (lastAddedFileNameNode != null)
                    status.LastAddedFileName = lastAddedFileNameNode.InnerText;
                var numberOfAddedFilesNode = xDoc.SelectSingleNode("/libraryStatus/numberOfAddedFiles");
                int tempNum = 0;
                if (numberOfAddedFilesNode != null)
                    int.TryParse(numberOfAddedFilesNode.InnerText, out tempNum);
                status.NumberOfAddedFiles = tempNum;
            }
            catch
            {
                return null;
            }

            return status;
        }

        public bool StartServiioService(bool waitForStart = true)
        {
            try
            {
                ServiceController sc = new ServiceController(ServiioServiceName);
                sc.Start();
                if (waitForStart)
                {
                    // wait for the service to start
                    sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 10));

                    // wait for Serviio's REST service to be available
                    int waitCount = 0;
                    while (!IsAvailable && waitCount++ < 12)
                        System.Threading.Thread.Sleep(250);
                    if (waitCount >= 12)
                        return false;
                    else
                        return true;
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool StopServiioService(bool waitForStop = true)
        {
            try
            {
                ServiceController sc = new ServiceController(ServiioServiceName);
                sc.Stop();
                if (waitForStop)
                {
                    sc.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 5));
                    System.Threading.Thread.Sleep(10);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool ChangeServiioServiceAccount(string username = "LocalSystem", string password = "")
        {
            if (String.IsNullOrEmpty(username))
            {
                username = "LocalSystem";
                password = "";
            }
            bool localSystem = "LocalSystem".Equals(username);

            if (!localSystem && !username.Contains("\\"))
                    username = ".\\" + username;

            try
            {
                string objPath = string.Format("Win32_Service.Name='{0}'", ServiioServiceName);
                object returnValue;
                using (ManagementObject service = new ManagementObject(new ManagementPath(objPath)))
                {
                    object[] wmiParams = new object[11];
                    wmiParams[5] = localSystem;
                    wmiParams[6] = username;
                    wmiParams[7] = password;
                    returnValue = service.InvokeMethod("Change", wmiParams);
                }
                return returnValue != null && "0".Equals(returnValue.ToString());
            }
            catch
            {
                return false;
            }
        }

        public bool ForceVideoFilesMetadataUpdate()
        {
            return DoAction("forceVideoFilesMetadataUpdate");
        }

        public bool ForceLibraryRefresh()
        {
            return DoAction("forceLibraryRefresh");
        }

        public bool ForceOnlineRepositoryRefresh(string repositoryId)
        {
            return DoAction("forceOnlineResourceRefresh", repositoryId);
        }

        public bool StartUpnpServer()
        {
            return DoAction("startServer");
        }

        public bool StopUpnpServer()
        {
            return DoAction("stopServer");
        }

        public bool ExitServiio()
        {
            return DoAction("exitServiio");
        }

        public bool AdvertiseService()
        {
            return DoAction("advertiseService");
        }

        private bool DoAction(string action, params string[] parameters)
        {
            try
            {
                XmlDocument xDoc = new XmlDocument();
                XmlDeclaration dec = xDoc.CreateXmlDeclaration("1.0", null, null);
                xDoc.AppendChild(dec);
                XmlElement xRoot = xDoc.CreateElement("action");
                xDoc.AppendChild(xRoot);

                XmlElement xName = xDoc.CreateElement("name");
                xName.InnerText = action;
                xRoot.AppendChild(xName);

                foreach (string param in parameters)
                {
                    XmlElement xParam = xDoc.CreateElement("parameter");
                    xParam.InnerText = param;
                    xRoot.AppendChild(xParam);
                }

                return RESTHelper.Post(Host + "/rest/action", xDoc);
            }
            catch
            {
                return false;
            }
        }
    }
}
